<?php

class SocketService
{
    private $address = '0.0.0.0';
    private $port = 8000;
    private $_sockets;
    private $_sockets_is_ws=[];

    public function __construct($address = '', $port = '')
    {
        if (!empty($address)) {
            $this->address = $address;
        }
        if (!empty($port)) {
            $this->port = $port;
        }
    }

    public function service()
    {
        $tcp = getprotobyname("tcp");
        $sock = socket_create(AF_INET, SOCK_STREAM, $tcp);
        socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
        if ($sock < 0) {
            throw new Exception("failed to create socket: " . socket_strerror($sock) . "\n");
        }
        socket_bind($sock, $this->address, $this->port);
        socket_listen($sock, $this->port);
        echo "【Listen on $this->address:$this->port 】 \n\n";
        $this->_sockets = $sock;
    }

    public function run()
    {
        $this->service();
        $clients[] = $this->_sockets;
        while (true) {
            $changes = $clients;
            $write = NULL;
            $except = NULL;
            $res=socket_select($changes, $write, $except, NULL);

            echo " res=".var_export($res,1).PHP_EOL;

            foreach ($changes as $key => $_sock) {
                if ($this->_sockets == $_sock) {
                    //判断是不是新接入的socket
                    if (($newClient = socket_accept($_sock)) === false) {
                        die('failed to accept socket: ' . socket_strerror($_sock) . "\n");
                    }


                    $this->handshaking($newClient);

                    socket_getpeername($newClient, $ip);
                    $clients[intval($newClient)] = $newClient;
                    echo "New client ip========{$ip} \n";

                } else {
                    socket_recv($_sock, $buffer, 1024, MSG_DONTWAIT);


                    if($this->_sockets_is_ws[intval($_sock)]){
                        $msg = $this->message($buffer);
                        echo "{$key}  msg=".var_export($msg,1);

                    }else{
                        echo 'buff='.strlen($buffer).":".var_export($buffer,1).PHP_EOL;
                    }


                    /*
                    fwrite(STDOUT, 'Please input a argument:');
                    $response = trim(fgets(STDIN));
                    $this->send($_sock, $response);
                    echo "{$key} response to Client:" . $response, "\n";
                    */

                }
            }
        }
    }

    /**
     * 握手处理
     * @param $newClient socket
     * @return int 接收到的信息
     */
    public function handshaking($newClient)
    {
        $line='';
       /* $len=0;
        $oneLen=1;
        while(false!==socket_recv($newClient,$buf,$oneLen,MSG_WAITALL)){
            $line.=$buf;
            echo "buf=".var_export($buf,1)."len=".$len.PHP_EOL;
            $len+=$oneLen;
            if($len>=600 ){
                break;
            }
        }*/

        $line=socket_read($newClient,1024);
        echo strlen($line).PHP_EOL;
        #echo "line=".var_export($line,1).PHP_EOL;
        if(empty($line)){
            $this->_sockets_is_ws[intval($newClient)]=false;
            return ;
        }
        $this->_sockets_is_ws[intval($newClient)]=true;
        $line = trim($line);
        $headers = array();
        $lines = preg_split("/\r\n/", $line);
        foreach ($lines as $line) {
            $line = chop($line);
            if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
                $headers[$matches[1]] = $matches[2];
            }
        }


        $secKey = $headers['Sec-WebSocket-Key'];
        $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
        $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "WebSocket-Origin: $this->address\r\n" .
            "WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n" .
            "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
        return socket_write($newClient, $upgrade, strlen($upgrade));
    }

    /**
     * 解析接收数据
     * @param $buffer
     * @return null|string
     */
    public function message($buffer)
    {
        $len = $masks = $data = $decoded = null;
        $len = ord($buffer[1]) & 127;
        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /**
     * 发送数据
     * @param $newClinet 新接入的socket
     * @param $msg 要发送的数据
     * @return int|string
     */
    public function send($newClinet, $msg)
    {
        $msg = $this->frame($msg);
        socket_write($newClinet, $msg, strlen($msg));
    }

    public function frame($s)
    {
        $a = str_split($s, 125);
        if (count($a) == 1) {
            return "\x81" . chr(strlen($a[0])) . $a[0];
        }
        $ns = "";
        foreach ($a as $o) {
            $ns .= "\x81" . chr(strlen($o)) . $o;
        }
        return $ns;
    }

    /**
     * 关闭socket
     */
    public function close()
    {
        return socket_close($this->_sockets);
    }
}

$sock = new SocketService();
$sock->run();